//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2020-06-15     ZhangYihua   first version
//
// Description  : transferring req/ack and info/resp between two clock domain. 
//                A transmission with src_nfr==1'b0 is least about
//               (SYNC_NUM_D2S+2) clk_src + (SYNC_NUM_S2D+2) clk_dst clock steps.
//                A transmission with src_nfr==1'b1 is least about 
//               (SYNC_NUM_D2S+3) clk_src + (SYNC_NUM_S2D+2) clk_dst clock steps.
//################################################################################

module sync_req #(
parameter           INFO_BW                 = 18,
parameter           RESP_BW                 = 18,
parameter           INFO_INPUT_HOLD         = 1'b1,     // set it 1'b0 for area saving if src_info/src_nfr is REG and stable during src_req==1'b1
parameter           RESP_INPUT_HOLD         = 1'b1,     // set it 1'b0 for area saving if dst_resp is REG and stable until next dst_ack==1'b1
parameter           SYNC_NUM_D2S            = 3,
parameter           SYNC_NUM_S2D            = 3
) ( 
input                                       rst_src_n,
input                                       clk_src,

input                                       src_req,    // keep src_req 1'b1 until src_ack==1'b1, and back-to-back is allowed
input               [INFO_BW-1:0]           src_info,   // forward information, keep stable when src_req==1'b1
input                                       src_nfr,    // 1'b1: need for response; 1'b0 not need for response
output                                      src_ack,    // deassert src_req immediatelly after src_ack==1'b1 unless a back-to-back new req come
output  reg         [RESP_BW-1:0]           src_resp,   // backward response, valid when src_ack==1'b1, and stable until next src_ack==1'b1

input                                       rst_dst_n,
input                                       clk_dst,

output  reg                                 dst_req,
output  reg         [INFO_BW-1:0]           dst_info,
output  reg                                 dst_nfr,    // 1'b1: need for response; 1'b0 not need for response
input                                       dst_ack,
input               [RESP_BW-1:0]           dst_resp
);

//################################################################################
// define local varialbe and localparam
//################################################################################
wire                                        dst_tog_d2s;
wire                                        trans_idle;
wire                                        src_start;
reg                                         src_tog;
reg                                         req_busy;
wire                                        info_done;
reg                                         resp_done;
wire                [INFO_BW-1:0]           src_info_sel;
wire                                        src_nfr_sel;
wire                                        src_tog_s2d;
wire                                        dst_start;
wire                                        dst_end;
reg                                         dst_tog;
wire                [RESP_BW-1:0]           dst_resp_sel;

//################################################################################
// main
//################################################################################

sync_dff #(
        .SYNC_NUM                       (SYNC_NUM_D2S                   ),
        .BW                             (1                              ),
        .INI                            (1'b0                           )
) u_tog_d2s ( 
        .rst_n                          (rst_src_n                      ),
        .clk                            (clk_src                        ),

        .d                              (dst_tog                        ),
        .q                              (dst_tog_d2s                    )
);

assign trans_idle = (src_tog==dst_tog_d2s) ? 1'b1 : 1'b0;

assign src_start  = src_req & (~req_busy) & (~resp_done);
assign info_done  = trans_idle & req_busy;
always@(posedge clk_src or negedge rst_src_n) begin
    if (rst_src_n==1'b0) begin
        src_tog  <=`U_DLY 1'b0;
        req_busy <=`U_DLY 1'b0;
    end else begin
        if (src_start==1'b1) begin
            src_tog  <=`U_DLY ~src_tog;
            req_busy <=`U_DLY 1'b1;
        end else if (info_done==1'b1)
            req_busy <=`U_DLY 1'b0;
        else
            ;
    end
end

always@(posedge clk_src or negedge rst_src_n) begin
    if (rst_src_n==1'b0) begin
        resp_done <=`U_DLY 1'b0;
        src_resp  <=`U_DLY {RESP_BW{1'b0}};
    end else begin
        if ((info_done==1'b1) && (src_nfr_sel==1'b1)) begin
            resp_done <=`U_DLY 1'b1;
            src_resp  <=`U_DLY dst_resp_sel;
        end else
            resp_done <=`U_DLY 1'b0;
    end
end
assign src_ack  = (info_done & (~src_nfr_sel)) | resp_done;

generate if (INFO_INPUT_HOLD==1'b0) begin:G_INFO_NHOLD

    assign src_info_sel = src_info;
    assign src_nfr_sel  = src_nfr;
    
end else begin:G_INFO_HOLD

    reg                 [INFO_BW-1:0]           src_info_hold;
    reg                                         src_nfr_hold;

    always@(posedge clk_src or negedge rst_src_n) begin
        if (rst_src_n==1'b0) begin
            src_info_hold <=`U_DLY {INFO_BW{1'b0}};
            src_nfr_hold  <=`U_DLY 1'b0;
        end else begin
            if (src_start==1'b1) begin
                src_info_hold <=`U_DLY src_info;
                src_nfr_hold  <=`U_DLY src_nfr;
            end else
                ;
        end
    end
    assign src_info_sel = src_info_hold;
    assign src_nfr_sel  = src_nfr_hold;
    
end endgenerate

// --------------------------------------------------------------------------------
// --------------------------------------------------------------------------------

sync_dff #(
        .SYNC_NUM                       (SYNC_NUM_S2D                   ),
        .BW                             (1                              ),
        .INI                            (1'b0                           )
) u_tog_s2d ( 
        .rst_n                          (rst_dst_n                      ),
        .clk                            (clk_dst                        ),

        .d                              (src_tog                        ),
        .q                              (src_tog_s2d                    )
);

assign dst_start = (dst_tog ^ src_tog_s2d) & (~dst_req); 
assign dst_end   = dst_req & dst_ack;
always@(posedge clk_dst or negedge rst_dst_n) begin
    if (rst_dst_n==1'b0) begin
        dst_tog  <=`U_DLY 1'b0;
        dst_req  <=`U_DLY 1'b0;
    end else begin
        if (dst_start==1'b1) begin
            dst_req  <=`U_DLY 1'b1;
        end else if (dst_end==1'b1) begin
            dst_tog  <=`U_DLY ~dst_tog;
            dst_req  <=`U_DLY 1'b0;
        end else
            ;
    end
end

always@(posedge clk_dst or negedge rst_dst_n) begin
    if (rst_dst_n==1'b0) begin
        dst_info <=`U_DLY {INFO_BW{1'b0}};
        dst_nfr  <=`U_DLY 1'b0;
    end else begin
        if (dst_start==1'b1) begin
            dst_info <=`U_DLY src_info_sel;
            dst_nfr  <=`U_DLY src_nfr_sel;
        end else
            ;
    end
end

generate if (RESP_INPUT_HOLD==1'b0) begin:G_RESP_NHOLD

    assign dst_resp_sel = dst_resp;
    
end else begin:G_RESP_HOLD

    reg                 [RESP_BW-1:0]           dst_resp_hold;

    always@(posedge clk_dst or negedge rst_dst_n) begin
        if (rst_dst_n==1'b0) begin
            dst_resp_hold <=`U_DLY {RESP_BW{1'b0}};
        end else begin
            if (dst_end==1'b1)
                dst_resp_hold <=`U_DLY dst_resp;
            else
                ;
        end
    end
    assign dst_resp_sel = dst_resp_hold;
    
end endgenerate

//################################################################################
// ASSERTION
//################################################################################

`ifdef CBB_ASSERT_ON
// synopsys translate_off

reg     REQ_CANCEL_CHKEN;
reg     INFO_STABLE_CHKEN;
reg     RESP_STABLE_CHKEN;

a_req_cancel: assert property (@(posedge clk_src) disable iff (!rst_src_n)
    ((REQ_CANCEL_CHKEN!==1'b0)&($fell(src_req)) |-> $past(src_ack))
) else begin
    $error("once src_req rise, it must keep 1 until src_ack rise.");
end

a_info_stable: assert property (@(posedge clk_src) disable iff (!rst_src_n)
    ((INFO_STABLE_CHKEN!==1'b0)&src_req&($stable(src_req))&(~$past(src_ack)) |-> ($stable(src_info))&($stable(src_nfr)))
) else begin
    $error("src_info and src_nfr must be stable during src_req==1'b1.");
end

a_resp_stable: assert property (@(posedge clk_dst) disable iff (!rst_dst_n)
    ((RESP_STABLE_CHKEN!==1'b0)&(RESP_INPUT_HOLD==1'b0)&(~dst_req) |-> ($stable(dst_resp)))
) else begin
    $error("dst_resp must be stable after dst_ack==1'b1.");
end

// synopsys translate_on
`endif

endmodule
